package web

import (
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"time"
	"webook/internal/domain"
	"webook/internal/service"
	"webook/pkg/logx"
)

const (
	emailRegexPattern    = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
	passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`
	nicknameRegexPattern = `^[\u4e00-\u9fa5a-zA-Z0-9_]{2,8}$`
	birthdayRegexPattern = `^(?:(?:19|20)\d{2})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`
	aboutMeRegexPattern  = `^[\u4e00-\u9fa5a-zA-Z0-9_]{0,512}$`
	biz                  = "login"
)

type UserHandler struct {
	EmailCompile    *regexp.Regexp
	PasswordCompile *regexp.Regexp
	NicknameCompile *regexp.Regexp
	BirthdayCompile *regexp.Regexp
	AboutMeCompile  *regexp.Regexp
	svc             service.IUserService
	codeSvc         service.CodeService
	jwtHdl          IJwtHandler
	logger          logx.LoggerX
}

func NewUserHandler(svc service.IUserService, codeSvc service.CodeService, jwtHdl IJwtHandler, logger logx.LoggerX) *UserHandler {
	return &UserHandler{
		EmailCompile:    regexp.MustCompile(emailRegexPattern, regexp.None),
		PasswordCompile: regexp.MustCompile(passwordRegexPattern, regexp.None),
		NicknameCompile: regexp.MustCompile(nicknameRegexPattern, regexp.None),
		BirthdayCompile: regexp.MustCompile(birthdayRegexPattern, regexp.None),
		AboutMeCompile:  regexp.MustCompile(aboutMeRegexPattern, regexp.None),
		svc:             svc,
		codeSvc:         codeSvc,
		jwtHdl:          jwtHdl,
		logger:          logger,
	}
}

func (u *UserHandler) UserRegister(ctx *gin.Context) {
	// 1. 接收用户请求参数
	type UserRegParam struct {
		Email           string `json:"email"`
		Password        string `json:"password"`
		ConfirmPassword string `json:"confirmPassword"`
	}
	var param UserRegParam
	// 如果 bind 出现解析异常会自动返回错误响应
	if err := ctx.Bind(&param); err != nil {
		return
	}
	// 2. 进行正则表达式校验
	// 2.1 校验邮箱是否符合格式
	email := param.Email
	isEmail, err := u.EmailCompile.MatchString(email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	if !isEmail {
		ctx.String(http.StatusOK, "非法邮箱格式!")
		return
	}
	// 2.2 校验用户密码输入是否一致
	pwd, confirmPwd := param.Password, param.ConfirmPassword
	if pwd != confirmPwd {
		ctx.String(http.StatusOK, "两次密码不一致!")
		return
	}
	// 2.3 校验密码格式
	isPwd, err := u.PasswordCompile.MatchString(pwd)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	if !isPwd {
		ctx.String(http.StatusOK, "密码必须包含字母、数字、特殊字符，并且不少于八位!")
		return
	}
	// 2.4 调用注册业务逻辑
	err = u.svc.SignUp(ctx, domain.User{
		Email:    email,
		Password: pwd,
	})
	if err == service.ErrUserDuplicate {
		ctx.String(http.StatusOK, "用户已存在!")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	ctx.String(http.StatusOK, "用户注册成功!")
}

func (u *UserHandler) UserLogin(ctx *gin.Context) {
	// 1. 接收前端请求
	type UserLoginParam struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	var userLoginParam UserLoginParam
	if err := ctx.Bind(&userLoginParam); err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	// 2. 处理登录业务
	user, err := u.svc.Login(ctx, domain.User{
		Email:    userLoginParam.Email,
		Password: userLoginParam.Password,
	})
	if err == service.ErrUserEmailNotExists || err == service.ErrUserDuplicate {
		ctx.String(http.StatusOK, "邮箱或密码错误!")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	// 3. 保持登录态
	session := sessions.Default(ctx)
	session.Set("userId", user.Id)
	// session还可以设置参数
	session.Options(sessions.Options{
		//Secure: true, // 只允许HTTPS请求
		//HttpOnly: true,
		MaxAge: 30 * 60, // 过期时间，单位为s
	})
	session.Save()
	ctx.String(http.StatusOK, "用户登录成功!")
}

func (u *UserHandler) UserLoginByJWT(ctx *gin.Context) {
	// 1. 接收前端请求
	type UserLoginParam struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	var userLoginParam UserLoginParam
	if err := ctx.Bind(&userLoginParam); err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	// 2. 处理登录业务
	user, err := u.svc.Login(ctx, domain.User{
		Email:    userLoginParam.Email,
		Password: userLoginParam.Password,
	})
	if err == service.ErrUserEmailNotExists ||
		err == service.ErrUserDuplicate ||
		err == service.ErrUserPasswordFalse {
		ctx.String(http.StatusOK, "邮箱或密码错误!")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	// 3. 保持登录态
	if err = u.jwtHdl.SetLoginToken(ctx, user.Id); err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	ctx.String(http.StatusOK, "用户登录成功!")
}

func (u *UserHandler) Edit(ctx *gin.Context) {
	// 1. 绑定参数
	type UserEditParam struct {
		Nickname string `json:"nickname"`
		Birthday string `json:"birthday"`
		AboutMe  string `json:"aboutMe"`
	}
	var userEditParam UserEditParam
	if err := ctx.Bind(&userEditParam); err != nil {
		return
	}
	// 2. 使用正则表达式进行校验
	var nickName = userEditParam.Nickname
	isNickname, err := u.NicknameCompile.MatchString(nickName)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	if !isNickname {
		ctx.String(http.StatusOK, "昵称格式非法!")
		return
	}
	var birthdayStr = userEditParam.Birthday
	isBirthday, err := u.BirthdayCompile.MatchString(birthdayStr)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	if !isBirthday {
		ctx.String(http.StatusOK, "生日日期格式非法!")
		return
	}
	birthday, err := time.Parse(time.DateOnly, birthdayStr)
	if err != nil {
		ctx.String(http.StatusOK, "生日日期格式非法!")
		return
	}
	var aboutMe = userEditParam.AboutMe
	isAboutMe, err := u.AboutMeCompile.MatchString(aboutMe)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	if !isAboutMe {
		ctx.String(http.StatusOK, "个人简介格式非法!")
		return
	}
	// 3. 获取登录用户id
	userIdStr, exists := ctx.Get("userId")
	if !exists {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	userId := userIdStr.(int64)
	err = u.svc.Edit(ctx, domain.User{
		Id:       userId,
		Nickname: nickName,
		Birthday: birthday,
		AboutMe:  aboutMe,
	})
	if err != nil {
		ctx.String(http.StatusOK, "系统错误!")
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "编辑成功!",
	})
}

func (u *UserHandler) Profile(ctx *gin.Context) {
	// 1. 获取用户id
	userIdStr, exists := ctx.Get("userId")
	if !exists {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	userId := userIdStr.(int64)
	// 2. 获取用户信息
	user, err := u.svc.GetUserById(ctx, userId)
	if err != nil {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	ctx.JSON(http.StatusOK, domain.User{
		Id:       userId,
		Email:    user.Email,
		Nickname: user.Nickname,
		Birthday: user.Birthday,
		AboutMe:  user.AboutMe,
	})
}

func (u *UserHandler) CodeSendBySms(ctx *gin.Context) {
	// 1. 接收用户参数
	type Req struct {
		Phone string `json:"phone"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}
	// 2. 调用code服务发送验证码
	err := u.codeSvc.Send(ctx, biz, req.Phone)
	if err == service.ErrCodeSendTooMany {
		ctx.JSON(http.StatusOK, gin.H{
			"code": 5,
			"msg":  "发送次数太频繁!",
		})
		u.logger.Warn("短信发送太频繁!")
		return
	}
	if err != nil {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	// 3. 发送成功
	ctx.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "发送验证码成功!",
	})
}

func (u *UserHandler) CodeVerifyBySms(ctx *gin.Context) {
	// 1. 接收用户参数
	type Req struct {
		Phone string `json:"phone"`
		Code  string `json:"code"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}
	// 2. 调用code服务验证验证码
	result, err := u.codeSvc.Verify(ctx, biz, req.Phone, req.Code)
	if err == service.ErrCodeVerifyTooMany {
		ctx.String(http.StatusInternalServerError, "验证码发送太频繁!")
		return
	}
	if err != nil {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		// 打印 ERROR 级别日志
		u.logger.Error("用户手机号登录失败!")
		return
	}
	if !result {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"code": 5,
			"msg":  "验证码错误!",
		})
		return
	}
	// 3. 查询并创建用户
	user, err := u.svc.FindOrCreate(ctx, req.Phone)
	if err == service.ErrUserDuplicate {
		ctx.JSON(http.StatusOK, gin.H{
			"code": 4,
			"msg":  "用户已存在!",
		})
		return
	}
	if err != nil {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	// 4. 设置jwt登录态
	err = u.jwtHdl.SetLoginToken(ctx, user.Id)
	if err != nil {
		ctx.String(http.StatusInternalServerError, "系统错误!")
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "验证码验证成功!",
	})
}

func (u *UserHandler) RefreshToken(ctx *gin.Context) {
	// 1. 抽取长token
	token, err := u.jwtHdl.ExtractToken(ctx)
	if err != nil {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	// 2. 校验长token
	var refreshClaims RefreshClaims
	parse, err := jwt.ParseWithClaims(token, &refreshClaims, func(token *jwt.Token) (interface{}, error) {
		return RefreshTokenKey, nil
	})
	if err != nil || !parse.Valid {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	// 解析UserAgent进行登录校验
	if refreshClaims.UserAgent != ctx.Request.UserAgent() {
		// TODO: 监控告警并埋点
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	// 校验ssid是否过期
	checkSsid, err := u.jwtHdl.CheckSsid(ctx, refreshClaims.Ssid)
	if err != nil || checkSsid {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	// 生成新的token并放置
	err = u.jwtHdl.SetJWTToken(ctx, refreshClaims.Uid, refreshClaims.Ssid)
	if err != nil {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "刷新成功!",
	})
}

func (u *UserHandler) LogOut(ctx *gin.Context) {
	err := u.jwtHdl.ClearToken(ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, gin.H{
			"code": 5,
			"msg":  "系统错误!",
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "退出登录成功!",
	})
}

func (u *UserHandler) RegisterRoutes(server *gin.Engine) {
	// 注册路由
	ug := server.Group("/users")
	ug.POST("/signup", u.UserRegister)
	//ug.POST("/login", u.UserLogin)
	ug.POST("/login", u.UserLoginByJWT)
	ug.POST("/edit", u.Edit)
	ug.GET("/profile", u.Profile)
	ug.POST("/code/sms/send", u.CodeSendBySms)
	ug.POST("/code/sms/verify", u.CodeVerifyBySms)
	ug.POST("/", u.CodeVerifyBySms)
	ug.GET("/refresh_token", u.RefreshToken)
	ug.GET("/logout", u.LogOut)
}
